﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using snat.model;
using System.Threading.Tasks;
using algorithms.utils.resultsets;

namespace algorithms.utils.metrics
{
    public static class NodeBetweeness
    {
        /// <summary>
        /// Given the output of Floyd Warshall, find the edge betweeness for all edges in the network
        /// </summary>
        /// <param name="connectivity">Floyd Warshall output</param>
        /// <returns></returns>
        public static Dictionary<Edge, int> ComputeEdgeBetweeness(ResultMatrix<BaseNode> connectivity) {
            HashSet<BaseNode> nodes = new HashSet<BaseNode>(connectivity.Keys);
            IEnumerable<Edge> edges =
                from e in NetworkModel.Instance.Edges
                where nodes.Contains(e.Target)
                && nodes.Contains(e.Source)
                select e;

            Dictionary<Edge, int> result = edges.ToDictionary(e => e, e => 0);

            Parallel.ForEach(edges, e => {
                BaseNode from = NetworkModel.Instance.Nodes[e.Source.ID];
                BaseNode to = NetworkModel.Instance.Nodes[e.Target.ID];
                if (from == to) return;

                foreach (BaseNode source in connectivity.AllReachingTo(from)) {
                    foreach (BaseNode target in connectivity.AllReachableFrom(to)) {
                        float minlength = connectivity[source, target];
                        if (float.IsInfinity(minlength)) continue; //todo test and remove
                        float pathlength = connectivity[source, from] + connectivity[from, to] + connectivity[to, target];
                        if (pathlength <= connectivity[source, target]) {
                            result[e]++;//does this work?
                        }
                    }
                }

            });

            return result;
        }
        /*PSEUDOCODE
         * 
         * foreach edge a->b
         *  if(dist(s,a) + dist(a,b) +dist(b, t) == dist(s,t)
         *      score++;
         * 
         * 
* foreach startnode
*  foreach endnode
*      foreach midnode
*          foreach allneighbors(midnode)
*              */


        public static Dictionary<K, int> ComputeNodeBetweeness<K>(ResultMatrix<K> connectivity) where K : BaseNode {
            Dictionary<K, int> betweenessCounts = connectivity.indexLookup.ToDictionary(e => e.Key, e => 0);
            Dictionary<Edge, int> edgeBetweeness = NetworkModel.Instance.Edges.ToDictionary(e => e, e => 0);



            List<K> allnodes = betweenessCounts.Keys.ToList();
            IEnumerable<K> midnodes = allnodes.Where(e => e.OutDegree > 0); //those that can be part of a chain

            Parallel.ForEach(midnodes, mid => {
                foreach (K start in allnodes) {
                    if (mid == start) continue;
                    float start_mid = connectivity[start, mid];
                    if (float.IsInfinity(start_mid)) continue;
                    foreach (K end in allnodes) {
                        if (start == end || mid == end) continue;
                        float mid_end = connectivity[mid, end];
                        if (float.IsInfinity(mid_end)) continue; //we now know that start_end also has a path
                        float start_end = connectivity[start, end];


                        if (start_mid + mid_end == start_end) {
                            betweenessCounts[mid] = betweenessCounts[mid] + 1;
                        }
                    }
                }
                Console.WriteLine("Processed {0}, value {1}", mid.Name, betweenessCounts[mid]);
            });

            /*PSEUDOCODE
             * foreach startnode
             *  foreach endnode
             *      foreach midnode
             *          foreach allneighbors(midnode)
             *              if(start->nid + mid->neighbor + neighbor ->end == start->end) edge++
            */
            return betweenessCounts;
        }
    }
}
